package com.example.ftpfilesync.service;

import com.example.ftpfilesync.config.FtpProperties;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Calendar;

@Service
public class FtpService {

    private static final Logger logger = LoggerFactory.getLogger(FtpService.class);

    private final FtpProperties ftpProperties;

    public FtpService(FtpProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    /**
     * 创建FTP客户端连接
     *
     * @return FTPClient实例
     * @throws IOException IO异常
     */
    public FTPClient createFTPClient() throws IOException {
        FTPClient ftpClient = new FTPClient();
        ftpClient.connect(ftpProperties.getFtp().getHost(), ftpProperties.getFtp().getPort());
        ftpClient.login(ftpProperties.getFtp().getUsername(), ftpProperties.getFtp().getPassword());
        ftpClient.enterLocalPassiveMode();
        ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
        return ftpClient;
    }

    /**
     * 递归复制目录及其内容
     *
     * @param sourceFtpClient 源FTP客户端
     * @param sourcePath      源路径
     * @param targetFtpClient 目标FTP客户端
     * @param targetPath      目标路径
     * @throws IOException IO异常
     */
    public void copyDirectory(FTPClient sourceFtpClient, String sourcePath,
                              FTPClient targetFtpClient, String targetPath) throws IOException {
        // 确保目标目录存在
        createDirectories(targetFtpClient, targetPath);

        // 获取源目录信息用于时间戳同步
        String sourceParentPath = getParentPath(sourcePath);
        String sourceDirName = getFileName(sourcePath);
        FTPFile sourceDirInfo = null;
        
        if (sourceParentPath != null && !sourceParentPath.isEmpty()) {
            sourceFtpClient.changeWorkingDirectory(sourceParentPath);
            FTPFile[] files = sourceFtpClient.listFiles(sourceDirName);
            if (files.length > 0) {
                sourceDirInfo = files[0];
            }
            // 切换回源目录以继续处理其内容
            sourceFtpClient.changeWorkingDirectory(sourcePath);
        }

        List<String> copiedFiles = new ArrayList<>();
        copyDirectoryRecursively(sourceFtpClient, sourcePath, targetFtpClient, targetPath, copiedFiles);

        // 记录详细日志
        logger.info("目录 {} 同步完成，共处理 {} 个文件/子目录", sourcePath, copiedFiles.size());
        for (String copiedFile : copiedFiles) {
            logger.info("  已同步: {}", copiedFile);
        }
        
        // 如果需要保持时间戳一致，则设置目标目录的时间戳与源目录一致
        if (ftpProperties.isPreserveTimestamps() && sourceDirInfo != null) {
            preserveDirectoryTimestamps(targetFtpClient, targetPath, sourceDirInfo);
        }
        
        logger.info("成功将目录 {} 复制到 {}", sourcePath, targetPath);
    }

    /**
     * 递归复制目录及其内容
     *
     * @param sourceFtpClient 源FTP客户端
     * @param sourcePath      源路径
     * @param targetFtpClient 目标FTP客户端
     * @param targetPath      目标路径
     * @param copiedFiles     已复制的文件列表
     * @throws IOException IO异常
     */
    private void copyDirectoryRecursively(FTPClient sourceFtpClient, String sourcePath,
                                          FTPClient targetFtpClient, String targetPath,
                                          List<String> copiedFiles) throws IOException {
        sourceFtpClient.changeWorkingDirectory(sourcePath);
        FTPFile[] files = sourceFtpClient.listFiles();

        for (FTPFile file : files) {
            if (".".equals(file.getName()) || "..".equals(file.getName())) {
                continue;
            }

            String sourceFilePath = sourcePath + "/" + file.getName();
            String targetFilePath = targetPath + "/" + file.getName();

            if (file.isFile()) {
                // 复制文件
                copyFile(sourceFtpClient, sourceFilePath, targetFtpClient, targetFilePath, file);
                copiedFiles.add("文件: " + sourceFilePath + " -> " + targetFilePath);
            } else if (file.isDirectory()) {
                // 递归复制目录
                copiedFiles.add("目录: " + sourceFilePath + " -> " + targetFilePath);
                copyDirectoryRecursively(sourceFtpClient, sourceFilePath, targetFtpClient, targetFilePath, copiedFiles);
            }
        }
    }

    /**
     * 复制单个文件
     *
     * @param sourceFtpClient 源FTP客户端
     * @param sourcePath      源文件路径
     * @param targetFtpClient 目标FTP客户端
     * @param targetPath      目标文件路径
     * @throws IOException IO异常
     */
    public void copyFile(FTPClient sourceFtpClient, String sourcePath,
                         FTPClient targetFtpClient, String targetPath) throws IOException {
        // 获取源文件信息
        sourceFtpClient.changeWorkingDirectory(getParentPath(sourcePath));
        FTPFile[] files = sourceFtpClient.listFiles(getFileName(sourcePath));
        FTPFile sourceFile = files.length > 0 ? files[0] : null;
        
        copyFile(sourceFtpClient, sourcePath, targetFtpClient, targetPath, sourceFile);
    }
    
    /**
     * 复制单个文件（带源文件信息）
     *
     * @param sourceFtpClient 源FTP客户端
     * @param sourcePath      源文件路径
     * @param targetFtpClient 目标FTP客户端
     * @param targetPath      目标文件路径
     * @param sourceFile      源文件信息
     * @throws IOException IO异常
     */
    public void copyFile(FTPClient sourceFtpClient, String sourcePath,
                         FTPClient targetFtpClient, String targetPath, FTPFile sourceFile) throws IOException {
        // 确保目标文件的父目录存在
        createDirectories(targetFtpClient, getParentPath(targetPath));
                         
        logger.info("正在复制文件: {} 到 {}", sourcePath, targetPath);
        try (InputStream inputStream = sourceFtpClient.retrieveFileStream(sourcePath)) {
            boolean success = targetFtpClient.storeFile(targetPath, inputStream);
            sourceFtpClient.completePendingCommand();
            if (success) {
                logger.info("成功复制文件: {}", sourcePath);
                
                // 如果需要保持时间戳一致，则设置目标文件的时间戳与源文件一致
                if (ftpProperties.isPreserveTimestamps() && sourceFile != null) {
                    preserveFileTimestamps(targetFtpClient, targetPath, sourceFile);
                }
            } else {
                logger.error("复制文件失败: {}", sourcePath);
            }
        }
    }

    /**
     * 创建目录（包括必要的父目录）
     *
     * @param ftpClient FTP客户端
     * @param path      要创建的目录路径
     * @throws IOException IO异常
     */
    private void createDirectories(FTPClient ftpClient, String path) throws IOException {
        if (path == null || path.isEmpty()) {
            return;
        }
        
        // 分割路径并逐级创建
        String[] dirs = path.split("/");
        String currentPath = "";
        
        for (String dir : dirs) {
            if (dir.isEmpty()) continue;
            currentPath += "/" + dir;
            
            // 检查目录是否存在，如果不存在则创建
            if (!directoryExists(ftpClient, currentPath)) {
                boolean created = ftpClient.makeDirectory(currentPath);
                if (created) {
                    logger.debug("成功创建目录: {}", currentPath);
                } else {
                    logger.debug("目录已存在或创建失败: {}", currentPath);
                }
            }
        }
    }

    /**
     * 检查目录是否存在
     *
     * @param ftpClient FTP客户端
     * @param path      目录路径
     * @return true表示目录存在，false表示不存在
     * @throws IOException IO异常
     */
    private boolean directoryExists(FTPClient ftpClient, String path) throws IOException {
        String current = ftpClient.printWorkingDirectory();
        boolean exists = ftpClient.changeWorkingDirectory(path);
        if (exists) {
            ftpClient.changeWorkingDirectory(current);
        }
        return exists;
    }
    
    /**
     * 获取路径的父目录
     * 
     * @param path 路径
     * @return 父目录路径
     */
    private String getParentPath(String path) {
        if (path == null || path.isEmpty()) {
            return "";
        }
        
        int lastSlashIndex = path.lastIndexOf("/");
        if (lastSlashIndex <= 0) {
            return "";
        }
        
        return path.substring(0, lastSlashIndex);
    }
    
    /**
     * 获取文件名
     * 
     * @param path 路径
     * @return 文件名
     */
    private String getFileName(String path) {
        if (path == null || path.isEmpty()) {
            return "";
        }
        
        int lastSlashIndex = path.lastIndexOf("/");
        if (lastSlashIndex < 0) {
            return path;
        }
        
        return path.substring(lastSlashIndex + 1);
    }
    
    /**
     * 保持源文件和目标文件的时间戳一致
     *
     * @param targetFtpClient 目标FTP客户端
     * @param targetPath      目标文件路径
     * @param sourceFile      源文件信息
     * @throws IOException IO异常
     */
    private void preserveFileTimestamps(FTPClient targetFtpClient, String targetPath, FTPFile sourceFile) throws IOException {
        if (sourceFile.getTimestamp() != null) {
            Calendar timestamp = sourceFile.getTimestamp();
            boolean success = targetFtpClient.setModificationTime(targetPath, 
                String.format("%04d%02d%02d%02d%02d%02d", 
                    timestamp.get(Calendar.YEAR),
                    timestamp.get(Calendar.MONTH) + 1,
                    timestamp.get(Calendar.DAY_OF_MONTH),
                    timestamp.get(Calendar.HOUR_OF_DAY),
                    timestamp.get(Calendar.MINUTE),
                    timestamp.get(Calendar.SECOND)));
            
            if (success) {
                logger.debug("已保持文件时间戳一致: {}", targetPath);
            } else {
                logger.debug("设置文件时间戳失败: {}", targetPath);
            }
        }
    }
    
    /**
     * 保持源目录和目标目录的时间戳一致
     *
     * @param targetFtpClient 目标FTP客户端
     * @param targetPath      目标目录路径
     * @param sourceDir       源目录信息
     * @throws IOException IO异常
     */
    private void preserveDirectoryTimestamps(FTPClient targetFtpClient, String targetPath, FTPFile sourceDir) throws IOException {
        if (sourceDir.getTimestamp() != null) {
            Calendar timestamp = sourceDir.getTimestamp();
            boolean success = targetFtpClient.setModificationTime(targetPath, 
                String.format("%04d%02d%02d%02d%02d%02d", 
                    timestamp.get(Calendar.YEAR),
                    timestamp.get(Calendar.MONTH) + 1,
                    timestamp.get(Calendar.DAY_OF_MONTH),
                    timestamp.get(Calendar.HOUR_OF_DAY),
                    timestamp.get(Calendar.MINUTE),
                    timestamp.get(Calendar.SECOND)));
            
            if (success) {
                logger.debug("已保持目录时间戳一致: {}", targetPath);
            } else {
                logger.debug("设置目录时间戳失败: {}", targetPath);
            }
        }
    }
}